<?php

namespace Yousheng\DatabaseToolbox\Json2Migration;

use Illuminate\Support\Str;

class SchemaParser {
    /**
     * Migration schema as an array
     * 
     * @var array
     */
    protected $schema;

    protected $tableModifiers=[
        'comment'=>'comment',
        'softdeletes'=>'softDeletes',
        'timestamps'=>'timestamps',
        'remembertoken'=>'rememberToken',
        'primary'=>'primary',
        'usecurrent'=>'useCurrent',
        'id'=>'id',
    ];

    /**
     * Create a new Schema parser instance
     * 
     * @param array $schema
     */
    public function __construct(Array $schema) {
        $this->schema = $schema;
    }

    /**
     * Parses the schema into migration methods
     * 
     * @return array
     */
    public function parse() {
        $tables = Helpers::justKeys($this->schema);
        
        foreach($this->schema as $table => $columns) {
            $tables[$table] = $this->generateMethods($columns);
        }
        return $tables;
    }

    /**
     * Loops through array of columns to parse
     * 
     * @return array
     */
    private function generateMethods(Array $columns) {
        $methods = [];
        $hasPrimary=false; // 如果有个字段是primary，就不添加 ->id()

        foreach($columns as $column => $parameters) {
            // 表级别的几个命令
            if($column==="table"){ // 表注释
                $map=$this->tableModifiers;
                foreach($parameters as $p){
                    $tableModifierName=strtolower($p[0]); // 表修饰名字
                    if(!array_key_exists($tableModifierName,$this->tableModifiers)){
                        echo $p[0]."暂不支持\n";
                        continue;
                    }
                    // 有primary的就不添加id
                    if($tableModifierName==='primary'){
                        $hasPrimary=true;
                    }
                    $tableModifierArgument=$p[1]??null; // 表修饰参数，以逗号分隔
                    if($tableModifierArgument===null){
                        $methods[]=sprintf('$table->%s();', $map[$tableModifierName]);
                    }else{
                        $para=trim($tableModifierArgument,"\,");
                        if(Str::contains($para,',')){
                            $para=explode(',',$para,2); // 处理 $table->primary(['id', 'parent_id']);这种
                            //多个参数考虑使用  "['" . implode("', '", $columnTypeParameters) . "']"
                            $methods[]=sprintf('$table->%s(["%s","%s"]);', $map[$tableModifierName], $para[0],$para[1]);
                        }else{
                            $methods[]=sprintf('$table->%s("%s");', $map[$tableModifierName], $tableModifierArgument);
                        }
                    }
                }
                continue;
            }
            // 列级别
            foreach ($parameters as $parameter){
                if($parameter[0]==='primary'){
                    $hasPrimary=true;
                }
            }
            $methods[] = $this->generateMethod($column, $parameters);

        }
        // 如果没有指定primary列,自动指定为id
        if(!$hasPrimary){
            array_unshift($methods,'$table->id();');
        }
        return $methods;
    }
    
    /**
     * Generates a migration method for the column
     */
    private function generateMethod(String $column, Array $parameters) {
        $columnType           = head(head($parameters));              // 第一个是字段类型
        $modifiers            = array_slice($parameters, 1);     //后面都是修饰，如 index,unique,default,comment等
        $columnTypeParameters = array_slice(head($parameters), 1);//类型参数，如head($parameters结果为["string","50" ]

        $this->checkIfValidColumnType($columnType);
        
        $baseMethod = $this->generateBaseMethod($column, $columnType, $columnTypeParameters);
        $modifiers  = $this->generateModifiers($modifiers);

        return $baseMethod . $modifiers . ';';
    }

    /**
     * Generate the base method for the migration
     * 
     * @param string $column
     * @param string $columnType
     * @param array $columnTypeParameters
     * @return string
     */
    private function generateBaseMethod($column, $columnType, $columnTypeParameters) {
        $validColumnType      = Parameters::getValidColumnType($columnType);
        $methodParameters     = Parameters::getParameters($validColumnType);
        $columnTypeParameters = !empty($columnTypeParameters) ? 
            explode(',', $columnTypeParameters[0]) :
            [];

        $customParameters = [];
        foreach($methodParameters as $k => $parameter) {
            if(!isset($columnTypeParameters[$k])) {
                $customParameters[] = $parameter;
                continue;
            }

            if(is_array($parameter)) {
                $customParameters[] = "['" . implode("', '", $columnTypeParameters) . "']";
            } else {
                $customParameters[] = $columnTypeParameters[$k];
            }
        }

        $joinedParameters = $this->joinParameters($customParameters);
        if(!blank($joinedParameters)) $joinedParameters = ', ' . $joinedParameters;

        return sprintf('$table->%s("%s"%s)', $validColumnType, $column, $joinedParameters);
    }

    /**
     * Generate the additional modifiers
     * 
     * @param array $modifiers
     * @return string
     */
    private function generateModifiers($modifiers) {
        $addedModifiers = [];
        foreach($modifiers as $modifier) {
            $modifierName   = head($modifier);
            $modifierParams = array_slice($modifier, 1);
            $params         = Parameters::getModifierParameters($modifierName);

            $extraParameters = [];
            foreach($params as $k => $param) {
                if(
                    is_string($param) &&
                    ($param == '' || $param == 'bool') && 
                    !isset($modifierParams[$k])
                ) {
                    throw new \Exception("修饰符 {$modifierName} 需要指定一个默认值.");
                }

                if(is_bool($param)) {
                    if(!isset($modifierParams[$k])) {
                        $extraParameters[] = $param ? "true" : "false";
                    } else {
                        $extraParameters[] = $modifierParams[$k];
                    }
                }
                
                if(is_string($param)) {
                    if($param == '') {
                        $extraParameters[] = "'{$modifierParams[$k]}'";
                    } else if($param == 'bool') {
                        if($modifierParams[$k] == 'true' || $modifierParams[$k] == 'false') {
                            $extraParameters[] = $modifierParams[$k];
                        } else {
                            $extraParameters[] = "'{$modifierParams[$k]}'";
                        }
                    } else {
                        $extraParameters[] = isset($modifierParams[$k]) ? "'{$modifierParams[$k]}'" : "'$param'";
                    } 
                }

            }

            $joinedExtraParameters = $this->joinParameters($extraParameters);

            $addedModifiers[] = sprintf('->%s(%s)', $modifierName, $joinedExtraParameters);
        }

        return implode('', $addedModifiers);
    }

    /**
     * Checks if the column type supplied is valid/permitted
     */
    private function checkIfValidColumnType(String $type) {
        if(!Parameters::validate($type)) {
//            throw new \Exception("Invalid column type supplied: {$type}");
            throw new \Exception("你指定的列类型无效: {$type}");
        }
    }

    /**
     * Joins parameters into a proper string
     * 
     * @param array $parameters
     * @return string
     */
    private function joinParameters($parameters) {
        return !empty($parameters) ? 
            implode(', ', $parameters) :
            '';
    }
}